﻿#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include "ngx_func.h"
#include "ngx_macro.h"
#include "ngx_c_conf.h"
//函数声明
static void ngx_start_worker_processes(int threadnums);
static int ngx_spawn_process(int threadnums,const char *pprocname);
static void ngx_worker_process_cycle(int inum,const char *pprocname);
static void ngx_worker_process_init(int inum);
//变量声明
static u_char  master_process[] = "master process";
/***************************************************************
 * @file       ngx_process_cycle.cxx
 * @brief      描述：创建worker子进程
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void ngx_master_process_cycle()
{
    /*信号集*/
    sigset_t set;
    /*清空信号集*/
    sigemptyset(&set);
    /*下列这些信号在执行本函数期间不希望收到(保护不希望由信号中断的代码临界区）
    建议fork()子进程时学习这种写法，防止信号的干扰*/
    sigaddset(&set, SIGCHLD);     //子进程状态改变
    sigaddset(&set, SIGALRM);     //定时器超时
    sigaddset(&set, SIGIO);       //异步I/O
    sigaddset(&set, SIGINT);      //终端中断符
    sigaddset(&set, SIGHUP);      //连接断开
    sigaddset(&set, SIGUSR1);     //用户定义信号
    sigaddset(&set, SIGUSR2);     //用户定义信号
    sigaddset(&set, SIGWINCH);    //终端窗口大小改变
    sigaddset(&set, SIGTERM);     //终止
    sigaddset(&set, SIGQUIT);     //终端退出符

    /*第一个参数用了SIG_BLOCK表明设置进程
    新的信号屏蔽字为当前信号屏蔽字和第二个参数指向的信号集的并集*/
    if(sigprocmask(SIG_BLOCK, &set, NULL) == -1) 
    {
        ngx_log_error_core(NGX_LOG_ALERT,errno,
            "ngx_master_process_cycle()中sigprocmask()失败!");
    }

    /*设置主进程标题*/
    size_t size;
    size = sizeof(master_process);
    size += g_argvneedmem;
    if(size < 1000) //长度小于这个，我才设置标题
    {
        char title[1000] = {0};
        strcpy(title,(const char *)master_process); //"master process"
        ngx_setproctitle(title); //设置标题
        /*设置标题时顺便记录下来进程名，进程id等信息到日志*/
        ngx_log_error_core(NGX_LOG_NOTICE,0,"%s %P\
            master进程启动并开始运行......!",title,ngx_pid);
    }

    /*从配置文件中得到要创建的worker进程数量*/
    CConfig *p_config = CConfig::GetInstance();
    int workprocess = p_config->GetIntDefault("WorkerProcesses",1); 

    /*这里要创建worker子进程*/
    ngx_start_worker_processes(workprocess);

    /*创建子进程后，父进程的执行流程会返回到这里，子进程不会走进来*/
    sigemptyset(&set);
    for(;;)
    {
        sleep(1);//休息1秒
    }
    return;
}
/***************************************************************
 * @file       ngx_c_socket.cxx
 * @brief      根据给定的参数创建指定数量的子进程，因为以后可能要扩展功能，增加参数，所以单独写成一个函数
 * @author     txj
 * @version    v1
 * @return
 * @date       2021/7/29
 **************************************************************/
static void ngx_start_worker_processes(int threadnums)
{
    int i4Index;
    /*master进程在走这个循环，来创建若干个子进程*/
    for(i4Index = 0; i4Index < threadnums; i4Index++)
    {
        ngx_spawn_process(i4Index,"worker process");
    }
    return;
}
/***************************************************************
 * @file       ngx_c_socket.cxx
 * @brief      产生一个子进程
 * @author     txj
 * @version    v1
 * @return
 * @date       2021/7/29
 **************************************************************/
static int ngx_spawn_process(int inum,const char *pprocname)
{
    pid_t  pid;
    pid = fork();
    switch (pid)
    {
    case -1:/*产生子进程失败*/
        ngx_log_error_core(NGX_LOG_ALERT,errno,
            "ngx_spawn_process()fork()产生子进程num=%d,procname=\"%s\"失败!",inum,pprocname);
        return -1;
    case 0:/*子进程分支*/
        /*因为是子进程了，所有原来的pid变成了父pid*/
        ngx_parent = ngx_pid;
        /*重新获取pid,即本子进程的pid*/
        ngx_pid = getpid();
        /*我希望所有worker子进程，在这个函数里不断循环着不出来，也就是说，子进程流程不往下边走*/
        ngx_worker_process_cycle(inum,pprocname);
        break;
    default:/*父进程分支，直接break;，流程往下走*/
        break;
    }
    /*父进程分支会走到这里，子进程流程不往下边走*/
    return pid;
}
/***************************************************************
 * @file       ngx_c_socket.cxx
 * @brief      worker子进程的功能函数，
 *             每个woker子进程，就在这里循环着了，无限循环处理网络事件
 *             和定时器事件以对外提供web服务，子进程分叉才会走到这里
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/5
 **************************************************************/
static void ngx_worker_process_cycle(int inum,const char *pprocname) 
{
    /*设置进程的类型，是worker进程*/
    ngx_process = NGX_PROCESS_WORKER;
    /*worker子进程初始化：
    1：恢复相关的信号
    2：创建多线程用于处理业务逻辑
    3：epoll初始化，为监听套接字增加读事件
    4：一些多线程间同步技术的初始化*/
    ngx_worker_process_init(inum);
    /*为worker子进程设置进程名*/
    ngx_setproctitle(pprocname);
    /*禁止输出缓冲区*/
    setvbuf(stdout,NULL,_IONBF,0);
    for(;;)
    {
        ngx_process_events_and_timers();
    }
    /*多线程的回收处理*/
    g_threadpool.StopAll();
    /*多线程当中的一些业务逻辑回收处理*/
    g_socket.Shutdown_subproc();
    return;
}
/***************************************************************
 * @file       ngx_process_cycle.cxx
 * @brief      子进程创建时调用本函数进行一些初始化工作
 * @author     txj
 * @version    v1
 * @return     返回-1，   子进程：返回0，父进程：返回1
 * @date       2021/7/29
 **************************************************************/
static void ngx_worker_process_init(int inum)
{
    sigset_t  set;
    sigemptyset(&set);
    /*原来是屏蔽那10个信号,防止fork()期间收到信号导致混乱,
    现在不再屏蔽任何信号/接收任何信号*/
    if(sigprocmask(SIG_SETMASK, &set, NULL) == -1)
    {
        ngx_log_error_core(NGX_LOG_ALERT,errno,
            "ngx_worker_process_init()中sigprocmask()失败!");
    }

    /*线程池代码，率先创建，至少要比和socket相关的内容优先*/
    CConfig *p_config = CConfig::GetInstance();
    /*处理接收到的消息的线程池中线程数量*/
    int tmpthreadnums = p_config->GetIntDefault("ProcMsgRecvWorkThreadCount",5); 
    if(g_threadpool.Create(tmpthreadnums) == false)
    {
        exit(-2);
    }
    /*休息1秒，等所有线程都创建完毕吧*/
    sleep(1);
    /*初始化子进程需要具备的一些多线程能力相关的信息*/
    if(g_socket.Initialize_subproc() == false)
    {
        exit(-2);
    }
    /*epoll初始化*/
    g_socket.ngx_epoll_init();
    return;
}
